package cn.asiontang.xposed.url_firewall;

import cn.asiontang.xposed.BaseXposedHookLoadPackage;
import cn.asiontang.xposed.LogEx;
import cn.asiontang.xposed.XposedUtils;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * <pre>
 * [?] 1.支持设置全局的 IP/URL 黑名单(所有APP皆生效)
 * [?] 2.支持设置是否监控APP(如可对淘宝,支付宝等APP取消监控)
 * [?] 3.供设置黑名单时的参考:
 * [?] 3.1 拿到IP后可以查询IP所属人
 * [?] 3.2 拿到网址后可以查询注册人
 * [?] 4.只保存URL的Path部分,貌似?号后面的部分太容易变化了,会导致记录数据暴增.
 * [?] 5.每种匹配规则记录命中次数
 * [?] 6.可以通过广播弹悬浮窗的方式模仿反编译SuperSu的实现方式.
 * </pre>
 * 参考资料:
 * <ul>
 * <li><a href='http://www.droidsec.cn/android-hook%E6%A1%86%E6%9E%B6xposed%E7%AF%87/?spm=5176.100239.blogcont59982.13.SALwrQ'>实战Hook
 * Android 中可能的出现 HTTP 请求</a></li>
 * <li><a href="https://github.com/Fuzion24/JustTrustMe/blob/master/app/src/main/java/just/trust/me/Main.java">JustTrustMe/Main.java
 * at master · Fuzion24/JustTrustMe · GitHub</a></li>
 * </ul>
 */
public class XposedHookLoadPackage extends BaseXposedHookLoadPackage
{
    private static final String TAG = XposedHookLoadPackage.class.getName();

    public boolean handleLoadPackage4release(final XC_LoadPackage.LoadPackageParam loadPackageParam)
    {
        if (!Config.get4hook().isEnable())
        {
            LogEx.log(TAG, "User Disabled.");
            return false;
        }

        //==========================================================================================
        // 需要 Hook 哪些应用?
        //==========================================================================================
        if (!loadPackageParam.packageName.equalsIgnoreCase(Config.get4hook().getHookStringPackageName()))
        {
            LogEx.log(TAG, String.format("HookString:Faild: getHookStringPackageName=%s No Match current %s", Config.get4hook().getHookStringPackageName(), loadPackageParam.packageName));
            return false;
        }

        //==========================================================================================
        // 需要解决的问题:
        //　　1. 直接在`handleLoadPackage` 里直接 `Hook`真正APP的某个特定类时会出现`java.lang.ClassNotFoundException: Didn't find class`错误
        //   　　> 原因是:默认首次加载的 `ClassLoader` 是加固厂家的固定 Application.只是负责解密用的,里面当然还没有APP真正的类相关信息的.
        //
        // 解决方案:
        //　　1. 既然Hook的时机默认调用太早了,那么想办法延后一点, 比如等到APP真正的Application.onCreate之后再 Hook 特定类.
        //==========================================================================================
        XposedUtils.realHookAllConstructors("android.app.Application", loadPackageParam.classLoader, new XC_MethodHook()
        {
            @Override
            protected void afterHookedMethod(final MethodHookParam param)
            {
                LogEx.log(TAG, loadPackageParam.packageName, "Application", param.thisObject, "Matched", "Hooking");

                handleLoadPackage4releaseWhenOnPerApplicationInit(loadPackageParam);

                LogEx.log(TAG, loadPackageParam.packageName, "Application", param.thisObject, "Matched", "Hooked");
            }
        });
        return true;
    }

    /**
     * 每个Application实例化时都会触发此Hook函数.
     */
    private void handleLoadPackage4releaseWhenOnPerApplicationInit(final XC_LoadPackage.LoadPackageParam loadPackageParam)
    {
        if (Config.get4hook().isEnableHookHttpClient())
        {
            XposedHookClass_HttpURLConnectionImpl.hook(loadPackageParam);

            XposedHookClass_okhttp3.hook(loadPackageParam);

            XposedHookClass_Socket.hook(loadPackageParam);
        }

        if (Config.get4hook().isEnableHookString())
        {
            XposedHookClass_String.hook(loadPackageParam);
        }
    }
}